package org.peterf.foxish.component.widget;

import org.peterf.foxish.camera.Camera;
import org.peterf.foxish.component.RectangleGroup;
import org.peterf.foxish.component.property.Focusable;
import org.peterf.foxish.component.updater.VibrateUpdater;
import org.peterf.foxish.engine.input.FingerEvent;
import org.peterf.foxish.engine.input.OnTouchListener;
import org.peterf.foxish.math.FloatUtil;
import org.peterf.foxish.texture.SlicedTexture;
import org.peterf.foxish.texture.TextureRepository;

import android.view.MotionEvent;

public abstract class VerticalRollingMenu extends RectangleGroup implements Focusable,
        OnTouchListener {

    private static enum State {
        FINGER_DOWN, FINGER_MOVED, IDLE, SELF_POSITIONING, VIBRATING,
    }

    private static float MOVED_THRESHOLD = 40.0f;

    private static float POSITION_SPEED = 8.0f;

    private static float POSITION_THRESHOLD = 1.0f;

    private static float VIBRATE_THRESHOLD = 10.0f;

    private int fingerId = -1;

    private float firstX;

    private float firstY;

    private float lastY;

    private float offsetX;

    private float offsetY = 0.0f;

    private State state = State.IDLE;

    private float targetOffsetY;

    public VerticalRollingMenu(int resId, int countY, float offsetX) {
        super(resId, 1, countY);
        this.offsetX = offsetX;
    }

    public boolean isInside(float x, float y) {
        x -= offsetX;
        int half = texture.getWidth() >> 1;
        return x <= half && x >= -half;
    }

    public abstract void onClick(int index);
    
    @Override
    public void onDraw(Camera camera, float elapsed) {
        switch (state) {
        case SELF_POSITIONING:
        case VIBRATING: {
            float delta = targetOffsetY - offsetY;
            if (state == State.SELF_POSITIONING
                    && FloatUtil.abs(delta) < VIBRATE_THRESHOLD) {
                int index = currentIndex();
                if (items[0][index].getUpdater() == null) {
                    items[0][index].setUpdater(new VibrateUpdater(1));
                }
                state = State.VIBRATING;
            }
            if (FloatUtil.abs(delta) < POSITION_THRESHOLD) {
                state = State.IDLE;
                offsetY = FloatUtil.period(targetOffsetY, texture.getHeight());
            } else {
                float range = delta * elapsed * POSITION_SPEED;
                if (FloatUtil.abs(range) > FloatUtil.abs(delta)) {
                    range = delta;
                }
                offsetY = offsetY + range;
            }
        }
        case IDLE:
        case FINGER_DOWN:
        case FINGER_MOVED: {
            float height = texture.getHeight();
            for (int i = 0; i < sliceY; ++i) {
                float dy = offsetY - i * texture.getCellHeight();
                dy = FloatUtil.period(dy, height);
                items[0][i].getMatrix().postTranslate(offsetX, dy);
            }
            super.onDraw(camera, elapsed);
            break;
        }
        }
    }

    public boolean onTouch(FingerEvent event) {
        switch (event.getActionMasked()) {
        case MotionEvent.ACTION_DOWN: {
            firstX = event.getX(0);
            firstY = lastY = event.getY(0);
            if (isInside(firstX, firstY)) {
                fingerId = event.getPointerId(0);
                state = State.FINGER_DOWN;
            } else {
                return false;
            }
            break;
        }
        case MotionEvent.ACTION_OUTSIDE:
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL: {
            if (state == State.IDLE || state == State.SELF_POSITIONING
                    || state == State.VIBRATING) {
                return false;
            }
            if (state == State.FINGER_DOWN) {
                onClick(currentIndex());
                state = State.IDLE;
            } else {
                int cell = texture.getCellHeight();
                targetOffsetY = FloatUtil.round(offsetY / cell) * cell;
                state = State.SELF_POSITIONING;
            }
            fingerId = -1;
            break;
        }
        case MotionEvent.ACTION_POINTER_UP: {
            if (fingerId == event.getPointerId(event.getActionIndex())) {
                if (state == State.IDLE || state == State.SELF_POSITIONING
                        || state == State.VIBRATING) {
                    return false;
                }
                state = State.FINGER_MOVED;
                int newIndex = event.getActionIndex() == 0 ? 1 : 0;
                fingerId = event.getPointerId(newIndex);
                lastY = event.getY(newIndex);
            }
            break;
        }
        case MotionEvent.ACTION_MOVE: {
            if (state == State.IDLE || state == State.SELF_POSITIONING
                    || state == State.VIBRATING) {
                return false;
            }
            int index = event.findPointerIndex(fingerId);
            float x = event.getX(index);
            float y = event.getY(index);
            if (state == State.FINGER_DOWN) {
                if (FloatUtil.distance2(x, y, firstX, firstY) >= MOVED_THRESHOLD) {
                    state = State.FINGER_MOVED;
                }
            }
            offsetY += (y - lastY);
            offsetY = FloatUtil.period(offsetY, texture.getHeight());
            lastY = y;
            break;
        }
        }
        return true;
    }

    private int currentIndex() {
        int index = FloatUtil.round(offsetY / texture.getCellHeight());
        index = (index + texture.getCountY()) % texture.getCountY();
        return index;
    }

}
